/*************************************************************************
	> File Name: threadPool.hpp
	> Author: H.M. Lau
	> Mail: hm.lau@outlook.com
	> Created Time: 2023/09/29 22:52
 ************************************************************************/

#pragma once

#include "comm.hpp"
#include "thread.hpp"
#include "lockGuard.hpp"
#include "log.hpp"

const int g_thread_num = 5;

template<class T>
class ThreadPool
{
public:
    static ThreadPool<T> *getThreadPool(int num = g_thread_num)
    {
        if (nullptr == _thread_ptr) {
            LockGuard lockGuard(&_mtx);
            if (nullptr == _thread_ptr) {
                _thread_ptr = new ThreadPool<T>(num);
            }
        }
        return _thread_ptr;
    }

    pthread_mutex_t *getMutex()
    {
        return &_mtx;
    }

    bool isEmpty()
    {
        return _tq.empty();
    }

    void waitCond()
    {
        pthread_cond_wait(&_cond, &_mtx);
    }

    T getTask()
    {
        T t = _tq.front();
        _tq.pop();
        return t;
    }

    static void* routine(void *args)
    {
        ThreadData *td = (ThreadData*)args;
        ThreadPool<T> *tp = (ThreadPool<T>*)td->_args;
        while (1) {
            T task;
            {
                LockGuard lockGuard(&_mtx);
                while (tp->isEmpty()) {
                    tp->waitCond();
                }
                task = tp->getTask();
            }
            task(td->_name);
        }
    }
    void run()
    {
        for (const auto &iter : _threads) {
            iter->start();
            logMessage(NORMAL, "%s %s", iter->name().c_str(), "Execute successfully.");
        }
    }

    void pushTask(const T &task)
    {
        LockGuard lockGuard(&_lock);
        _tq.push(task);
        pthread_cond_signal(&_cond);
    }

    ~ThreadPool()
    {
        for (auto &iter : _threads) {
            iter->join();
            delete iter;
        }
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_cond);
    }


private:
    ThreadPool(int thread_num = g_thread_num):_num(thread_num)
    {
        pthread_mutex_init(&_lock, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for (int i = 0; i <= _num; ++i) {
            _threads.push_back(new Thread(i, routine, this));
        }
    }
    ThreadPool(const ThreadPool<T> &other) = delete;
    const ThreadPool<T> &operator=(const ThreadPool<T> &other) = delete;

    std::vector<Thread*> _threads;
    int _num;
    std::queue<T> _tq;
    static ThreadPool<T> *_thread_ptr;
    static pthread_mutex_t _mtx;
    pthread_mutex_t _lock;
    pthread_cond_t _cond;
};
template<typename T>
ThreadPool<T> *ThreadPool<T>::_thread_ptr = nullptr;

template<typename T>
pthread_mutex_t ThreadPool<T>::_mtx = PTHREAD_MUTEX_INITIALIZER;
